package instrumentation;


import java.util.*;


import util.DebugConfig;
import util.GAConfig;
import util.Log;
import ga.Individual;
import operations.Operation;
import instrumentation.CoverageBranchTarget;

public class TrackerCoverage {

	private static TrackerCoverage tracker;
	// Branch Coverage
	public static  Set <Integer> branches= new HashSet<Integer>();
	public static  Map<Integer,Individual> branchCoveredByIndividual;
	public Map<Integer,Individual> suiteBR= new HashMap<Integer, Individual>() ;

	private  CoverageBranchTarget condTarget ;
	private  Map<Integer,CoverageBranchTarget> trackers = new LinkedHashMap<Integer,CoverageBranchTarget>();

	/** condition coverage */
	private static Set <Integer> covBranches;
	public Map<Integer,CoverageBranchTarget> getTrackers(){
		return this.trackers;
	}
	public Map<Integer,Individual> getSuite(){
		return this.suiteBR;
	}
	public String getCovBranches(){
		StringBuilder st =new StringBuilder();
		st.append("covBranches: ");
		for(Integer i:this.covBranches)
			st.append( " "+ i);

		return st.toString();
	}

	public void setTrackerToNull(){
		tracker=null;
	}
	
    public static TrackerCoverage getTracker() {
		if(tracker == null){
			tracker = new TrackerCoverage();
			covBranches = new HashSet<Integer>();
			branchCoveredByIndividual = new HashMap<Integer, Individual>() ;
		}
		return tracker;
	}


	public void reset() {
		this.branches.clear();
		this.condTarget=null;
		this.suiteBR.clear();
		this.trackers.clear();

	}
	public Map<Integer,Individual> getBranchCoveredByIndividual(){
		return this.branchCoveredByIndividual;
	}


    public  void branchHit(int branchId) {
        if(!this.covBranches.contains(branchId)&&!trackers.containsKey(branchId)){
        	this.condTarget = new CoverageBranchTarget(branchId);
        	this.trackers.put(branchId,condTarget);
        }

    }
	public void branchCovered(int branchId) {
//		if(branchId==8||branchId==8)
//			System.err.println("Branch Covered "+ branchId);
		try {
		this.branches.add(branchId);
		this.covBranches.add(branchId);
		this.trackers.remove(branchId);
		} catch (Throwable e) {
			System.err.println("branchCovered: " + e.getMessage());
			System.exit(1);
		}

	}

	public  int getConditionTargetId(int branchId) {
		if(!covBranches.contains(branchId)){
			if(this.trackers.containsKey(branchId)){
				this.condTarget=(CoverageBranchTarget)this.trackers.get(branchId);
				if(this.condTarget.getBranchId()==branchId)
					return branchId;
			}
		}
		return -1;
	}
	public void setConditionTargetDistance(double distance) {
		this.condTarget.setDistance(distance);
	}

	//null==null=> 1(v1=0; v2=0)+1=>1; quality=0.5 Reference" Arucuri Thesis Page: 26
	public void calculateConditionTargetDistance(double v1, double v2) {
		this.condTarget.setDistance((Math.abs(v1 - v2)));
		if(this.condTarget.getBranchId()==-3)
			System.out.println("Distance: "+this.condTarget.getQuality() +" v1: "+ v1+ " v2: "+v2);

	}

	public float getTotalCoveredBranch(List<Integer> branchs) {
		float CoveredBR=0;
		List<Integer> keys = new ArrayList<Integer>( this.branchCoveredByIndividual.keySet());   
		for( int i=0; i<keys.size();i++){			
				if( branchs.contains(keys.get(i)))
					CoveredBR++;
		}
		if(CoveredBR==-1){
			 if (GAConfig.SaveData)	Log.log(this.getCovBranches());
			 if (GAConfig.SaveData)	Log.log("TrackerCoverage.branchCoveredByIndividual :"+ keys.toString());
			 if (GAConfig.SaveData)	Log.log(branchs.toString());
			 Log.log("----------------------------------- CoveredBR= "+ CoveredBR);

		}
		return CoveredBR;	
	}

	public float [] calculateFitness(Individual test,Set<Integer> branchToBeCovered) {
		float [] fitness= new float[2]; 
		float totalCoverageBranch=0.0f;
		float branchDistance=0;
		//calculate number of covered branches
		try
		{
		for (Integer branchId :this.branches) {
			if(!branchToBeCovered.contains(branchId))continue;
				totalCoverageBranch++;		
				if(!this.branchCoveredByIndividual.containsKey(branchId)){
					this.branchCoveredByIndividual.put(branchId, test.clone());//)[branchId]=test.clone();
				}
				
			}
		//calculate number of hit  branches----> branch distances
		for (Map.Entry<Integer, CoverageBranchTarget> entry : this.trackers.entrySet()) {
			this.condTarget=(CoverageBranchTarget)entry.getValue();
				if(this.condTarget!=null)
				if(this.condTarget.getQuality()!=1)
					branchDistance+=this.condTarget.getQuality();
			}

	//	fitness[0]=totalCoverageBranch+(0.5f * (1 / (1.0f + test.getSize())));
		 fitness[0]=0;//(1 / (1.0f + test.getSize()));

		 fitness[1]=branchDistance;

		 
	} catch (Throwable e) {
		System.err.println("calculateFitness: " + e.getMessage());
		System.exit(1);
	}		 
		 
		return fitness;
		
	}	

	
	public  void summarizeCoverage(List<Integer> branchs) {
		HashSet<String> individualToString= new HashSet<String>();
				for (Integer i : this.branchCoveredByIndividual.keySet()) {
					if(! branchs.contains(i))continue;
					Individual test=	this.branchCoveredByIndividual.get(i);
					if(!individualToString.contains(test.toString()))
						suiteBR.put(suiteBR.size()+1,test);
				}
//				System.out.println ("SUMMARY: " + suiteBR.size() +" tests needed for total branch coverage:");
			    if(GAConfig.SaveData)
			    	Log.log("SUMMARY: " + suiteBR.size() +" tests needed for total branch coverage:");
			    

	}

	  public static final String lineSep = System.getProperty("line.separator");
		public String displayCoveredBranchs(List<Integer> branchs) {			int CoveredBR=0;
		int unCoveredBR=0,idx=0;

		StringBuilder st =new StringBuilder();
		 int [] BRCovered = new int[branchs.size()];
		 int [] BRUnCovered = new int[branchs.size()];
		List<Integer> keys = new ArrayList<Integer>( this.branchCoveredByIndividual.keySet());   
		for( int i=0; i<branchs.size();i++){			
		//	if(! branchs.contains(keys.get(i)))continue;
		//	CoverageBranchTarget condTarget=(CoverageBranchTarget)this.branchCoveredByIndividual.get(keys.get(i));
			if(keys.contains(branchs.get(i))){
				CoveredBR++;
				BRCovered[idx++]=branchs.get(i);
			}
			else{
				BRUnCovered[unCoveredBR]=branchs.get(i);
				unCoveredBR++;
			}

		}
//		st.append("Total Branches  ( " + keys.size()  +" ):");
//		st.append(lineSep);
		st.append("BR Covered SUMMARY ( " + CoveredBR  +" ):");
		st.append(lineSep);
		 if (!GAConfig.Silent) {
			 if(CoveredBR<=200){
			Arrays.sort(BRCovered);
			for (int i = 0; i < BRCovered.length; i++)
				if (BRCovered[i] != 0)
					st.append("B" + BRCovered[i] + " ");
			st.append(lineSep);
		}
		 }
		 st.append("BR unCovered SUMMARY ( " + unCoveredBR  +" ): ");
		 st.append(lineSep);
		 if(unCoveredBR<=200){
			Arrays.sort(BRUnCovered);
			for (int i = 0; i < BRUnCovered.length; i++)
				if (BRUnCovered[i] != 0){
					st.append("B" + BRUnCovered[i] + " ");
//					if(this.branchNotCoveredByIndividual.get(BRUnCovered[i])!=null)
//						st.append(" " + this.branchNotCoveredByIndividual.get( BRUnCovered[i]).getBranchDistance() + " "+this.branchNotCoveredByIndividual.get( BRUnCovered[i]).getSize());
//					st.append(lineSep);

				}
			st.append(lineSep);
		 }
		return st.toString(); 
}

	


	
}